L14: Bash skripty – funkce

linux.edumach.cz



1. Příprava

⚠️ Pokud nemáte stažený Git repozitář z lekce "L11: Bash skripty -- echo, proměnné", stáhněte si ho. Postup je hned na začátku.

2. Východiska

Funkce zjednodušují a zpřehledňují skripty tím, že rozdělují program na menší části. Každá funkce provede definované příkazy a může vracet hodnotu. Funkce lze volat z hlavního programu, což umožňuje rozložit úkoly na části, které můžete v kódu opakovaně použít.

Před pokračováním si uvedeme příklad funkce, která pouze vypíše text (skript funkce.sh):

#!/bin/bash

hello()
{
  echo "Nyní jsme ve funkci hello()"
}

echo "Nyní se zavolá funkce hello()..."
hello
echo "Nyní jsme zase mimo funkci hello()"

Funkce zde v našem konkrétním případě plní účel vypsání textu, který nás pouze informuje o tom, že je funkce prováděna.

Funkce samozřejmě může dělat mnohem komplikovanější úlohy, ale právě zde jen vytiskne text. Funkci vždy voláme napsáním jejího jména do programu, jejíž jméno může být libovolné. V našem programu se jmenuje hello, a volá se:

hello 

Když je tento řádek vykonáván, bash prohledá skript, zda-li je v něm funkce hello(). Nalezne-li jí, tak provede příkazy, které jsou v ní zapsány.

Samotná funkce je určena svým názvem, po kterém následuje dvojice kulatých závorek ( ). Blok příkazů ve funkci je uzavřen složenými závorkami { }. Ty značí začátek a konec funkce. Veškerý kód funkce bude vykonán pouze, bude-li funkce zavolána.

Funkce by také měla být vždy definována ještě před tím, než bude zavolána:

hello()
{
  echo "Nyní jsme ve funkci hello()"
}

Je též přípustné, že před názvem funkce může být slovo function:

function hello()
{
  echo "Nyní jsme ve funkci hello()"
} 

2.1. Jak to tedy funguje?

Skript se začne normálně provádět od začátku, jak jsme zvyklí, takže nic nového. Když ale narazí na konstrukci hello() nebo function hello() ví, že se jedná o definici funkce jménem hello. Zapamatuje si, že hello odkazuje na funkci a pokračuje v provádění příkazů po odpovídající složené závorce.

Dříve než funkci vyvoláte, vždy ji musíte nejprve definovat, podobně jako v jakémkoliv jiném jazyce. Protože se všechny skripty provádějí od začátku, umístíte všechny funkce před její první volání.

Při volání funkce jsou poziční parametry skriptu $*, $@, $#, $1, $2 atd. nahrazeny parametry funkce. Pomocí nich pak čtete parametry předané funkci. Po skončení funkce jsou hodnoty všech parametrů zase obnoveny. Tyto parametry se používají stejně jako proměnné. Příklad -- skript pozdrav.sh:

#!/bin/bash

pozdrav() 
{
    local jmeno=$1
    local denni_doba=$2
    echo "Dobre $denni_doba, $jmeno!"
}

# Volání funkce s konkrétními parametry 
pozdrav "Pavle" "rano" 

V tomto příkladu jsou $1 a $2 v těle funkce pozdrav nahrazeny skutečnými parametry předanými funkci. Funkce poté vypíše příslušný pozdrav. Po skončení funkce jsou hodnoty $1 a $2 zase obnoveny, takže neovlivní žádné proměnné mimo rozsah funkce.

2.2. Návratové hodnoty

Funkce mohou pomocí příkazu return vracet číselnou hodnotu. Pokud je potřeba vrátit řetězec, uloží se do proměnné, kterou lze po skončení funkce použít. Případně můžete řetězec vypsat příkazem echo a výsledek odchytit jako v následujícím příkladu:

#!/bin/bash

foo()
{
  echo Ahoj
}

... 

result="$(hello)" 

3. Lokální proměnné

Uvnitř shellu můžete za pomoci klíčového slova local deklarovat lokální proměnné. Proměnná pak existuje jen v rámci dané funkce. Jinak může funkce přistupovat k jiným proměnným shellu, které jsou v podstatě globální. Pokud má lokální proměnná stejný název jako globální, pak tuto proměnnou potlačí, ale jen v rámci příslušné funkce. Abychom si to demonstrovali, ukážeme si následující příklad - skript local.sh: :

#!/bin/bash

vzorovy_text="globální proměnná" 

foo()
{
  local vzorovy_text="lokální proměnná" 
  echo "Provádí se funkce foo" 
  echo $vzorovy_text
} 

echo "Začátek skriptu" 
echo $vzorovy_text 

foo

echo "Konec skriptu"
echo $vzorovy_text

exit 0 

Pokud funkce neobsahuje příkaz return, vrací návratový kód posledního provedeného příkazu.

V následujícím příkladu si ukážeme způsob předávání parametrů funkci a také způsob jakým mohou funkce vracet hodnoty true nebo false. Skript yes_no.sh:

#!/bin/bash

yes_or_no()
{
  echo "Je tvé jméno $* ?"
  while true
  do
    echo -n "Napiš ano nebo ne: "
    read x
    case "$x" in
      a | ano ) return 0;;
      n | ne ) return 1;;
      * ) echo "Napiš ano nebo ne"
    esac
  done
}

echo "Původní parametr je $*"

if yes_or_no "$1"
then
  echo "Ahoj $1, pěkné jméno"
else
  echo "Nikdy"
fi
exit 0

Tento příklad funguje tak, že po spuštění skriptu je definována funkce yes_or_no, ale není provedena. V příkazu if provede skript funkci yes_or_no, které předá jako parametry zbytek řádku, kde zápis $1 nahradí prvním parametrem skriptu. Funkce pak pracuje s těmito parametry, které jsou nyní uloženy v pozičních proměnných $1, $2 atd. a vrátí hodnotu volajícímu příkazu. Příkaz if v závislosti na návratové hodnotě provede příslušní příkaz.

4. 💾 Úlohy

Tři úkoly na procvičení práce s funkcemi v Bashi:

4.1. Kalkulačka (calc.sh)

Vytvoř funkci kalkulacka, která bude přijímat dva číselné argumenty a jeden operátor (+, -, *, /). Funkce provede příslušnou operaci a výsledek vypíše. Dělení bude celočíselné, o čemž skript uživatele informuje -- za výsledkem bude (int).

Příklad použití:

./calc.sh 5 + 3
./calc.sh 5 / 2

Výstup:

Výsledek operace 5 + 3 je 8
Výsledek operace 5 / 2 je 2 (int)

Nápověda:

4.2. Kontrola hesla (pass.sh)

Zadání: Vytvořte funkci kontrola_hesla, která zkontroluje, zda zadané heslo splňuje určitá kritéria (např. minimální délka, obsah číslic a písmen). Funkce vrátí hodnotu 0 (úspěch) nebo 1 (neúspěch).

Možná kostra skriptu:

#!/bin/bash

kontrola_hesla() {
    if [[ ${#1} -lt 8 ]]; then
        return 1 
    fi
    # Další podmínky pro číslice, speciální znaky atd.
    return 0
}

# Použití:
if kontrola_hesla "MojeHeslo123"; then
    echo "Heslo je v pořádku."
else
    echo "Heslo nesplňuje požadavky."
fi

4.3. Generátor hesel (rand.sh)

Zadání: Vytvoř funkci generuj_heslo, která přijme délku hesla a generuje náhodné heslo složené z malých a velkých písmen a číslic.

heslo=$(generuj_heslo 12)
echo "Vygenerované heslo: $heslo"

Nápověda:

Možná kostra skriptu:

#!/bin/bash

generuj_heslo() {
    heslo=""
    for i in $(seq 1 $1); do
        heslo="$heslo${RANDOM:0:1}"
    done
    echo "$heslo"
}

# Použití:
heslo=$(generuj_heslo 12)
echo "Vygenerované heslo: $heslo"